package httpserver;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.zip.GZIPInputStream;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;

public class HttpServer {

	private static final String			http11						= "HTTP/1.1";
	private static final String			http10						= "HTTP/1.0";
	private static final Set<String>	ALLOWED_PROTOCOLS			= new HashSet<String>(Arrays.asList(http11, http10));
	private static final String			CONTENT_LENGTH_HEADER		= "Content-Length";
	private static final String			TRANSFER_ENCODING_HEADER	= "Transfer-Encoding";

	static final int					PORT						= 80;
	BufferedReader						dataInputStream				= null;

	public static void main(String[] args) throws IOException {

		ServerSocket serverSocket = new ServerSocket(PORT);
		Socket socket = serverSocket.accept();
		try {
			BufferedReader dataInputStream = new BufferedReader(new InputStreamReader(socket.getInputStream(), "US-ASCII"));
			BufferedOutputStream dataOutputStream = new BufferedOutputStream(socket.getOutputStream());

			String firstLine = dataInputStream.readLine();
			StringTokenizer firstLineParser = new StringTokenizer(firstLine);
			String method = firstLineParser.nextToken().toUpperCase();
			String url = firstLineParser.nextToken();
			String protocol = firstLineParser.nextToken();
			if (!ALLOWED_PROTOCOLS.contains(protocol)) {
				throw new HttpException("Invalid protocol: " + protocol);
			}

			Map<String, List<String>> headers = new HashMap<String, List<String>>();
			String headerLine;
			while (!"".equals((headerLine = dataInputStream.readLine()))) {
				String[] headerTokens = headerLine.split(":");
				List<String> headerValues = headers.get(headerTokens[0]);
				if (headerValues == null) {
					headerValues = new ArrayList<String>();
					headers.put(headerTokens[0], headerValues);
				}
				headerValues.add(headerTokens[1]);
			}

			int contentLength = 0;
			if (headers.containsKey(CONTENT_LENGTH_HEADER)) {
				contentLength = Integer.parseInt(headers.get(CONTENT_LENGTH_HEADER).get(0));
			}
			TransferEncoding encoding = null;
			if (headers.containsKey(TRANSFER_ENCODING_HEADER)) {
				encoding = TransferEncoding.valueOf(headers.get(TRANSFER_ENCODING_HEADER).get(0));
			}
			InputStream is = socket.getInputStream();
			if (TransferEncoding.GZIP.equals(encoding)) {
				is = new GZIPInputStream(is);
			}
			ByteArrayOutputStream baos = new ByteArrayOutputStream(contentLength);
			IOUtils.copy(is, baos);
			byte[] body = baos.toByteArray();

			if (TransferEncoding.BASE64.equals(encoding)) {
				body = Base64.decodeBase64(body);
			}

			Map<String, List<String>> responseHeaders = new HashMap<String, List<String>>();
			ByteArrayOutputStream responseBody = new ByteArrayOutputStream();
			HttpResponse response = new HttpResponseImpl(responseBody, responseHeaders);
			HttpRequest request = new HttpRequestImpl(method, url, protocol, headers, new ByteArrayInputStream(body), response,
					((InetSocketAddress) socket.getRemoteSocketAddress()).getHostString(), ((InetSocketAddress) socket.getRemoteSocketAddress()).getPort());

			servletContainer.processRequest(request);
			// TODO:
		} catch (HttpException e) {

		}
		socket.close();
		serverSocket.close();
	}
}
